package com.didd.core.mybatis.query;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.Query;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.conditions.AbstractChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.ChainQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.additional.query.impl.LambdaQueryChainWrapper;
import com.didd.core.common.base.SimplePage;
import com.didd.core.common.utils.BeanUtil;
import com.didd.core.exception.BizException;
import lombok.extern.slf4j.Slf4j;

import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;

/**
 * * 自定义实现 LambdaQueryChainWrapper
 * * <p>
 * * {@link LambdaQueryChainWrapper} 查看对应实例
 *
 * @author diddd
 * @Created 2020/11/4 9:58
 * @see LambdaQueryChainWrapper
 */
@Slf4j
public class DidddQueryChainWrapper<T> extends AbstractChainWrapper<T, SFunction<T, ?>, DidddQueryChainWrapper<T>, LambdaQueryWrapper<T>>
        implements ChainQuery<T>, Query<DidddQueryChainWrapper<T>, T, SFunction<T, ?>> {

    private BaseMapper<T> baseMapper;

    public DidddQueryChainWrapper(BaseMapper<T> baseMapper) {
        super();
        this.baseMapper = baseMapper;
        super.wrapperChildren = new LambdaQueryWrapper<>();
    }

    @SafeVarargs
    @Override
    public final DidddQueryChainWrapper<T> select(SFunction<T, ?>... columns) {
        wrapperChildren.select(columns);
        return typedThis;
    }

    @Override
    public DidddQueryChainWrapper<T> select(Predicate<TableFieldInfo> predicate) {
        wrapperChildren.select(predicate);
        return typedThis;
    }

    @Override
    public DidddQueryChainWrapper<T> select(Class<T> entityClass, Predicate<TableFieldInfo> predicate) {
        wrapperChildren.select(entityClass, predicate);
        return typedThis;
    }

    @Override
    public String getSqlSelect() {
        throw ExceptionUtils.mpe("can not use this method for \"%s\"", "getSqlSelect");
    }

    @Override
    public BaseMapper<T> getBaseMapper() {
        return baseMapper;
    }

    @Override
    @SuppressWarnings("unchecked")
    public AbstractWrapper getWrapper() {
        return super.getWrapper();
    }


    /** ********************* 自定义方法  ************************/
    /**
     * 查询 单条
     *
     * @param warnMessage 自定义异常信息
     * @param isThrowable 是否抛出异常
     * @return
     */
    public T findOne(Boolean isThrowable, String warnMessage) {
        T entity = this.one();
        if (entity != null) {
            return entity;
        }
        if (isThrowable) {
            log.error("[DiddQueryChainWrapper][数据库单表数据查询]查询数据不存在:condition:[{}]", formatFilterCondition());
            throw new BizException(warnMessage);
        }
        return null;
    }

    /**
     * 查询 单条
     *
     * @param warnMessage 自定义异常信息
     * @param isThrowable 是否抛出异常
     * @return
     */
    public T filterOne(Boolean isThrowable, String warnMessage) {
        List<T> list = this.list();
        if (CollectionUtils.isNotEmpty(list)) {
            return list.get(0);
        }
        if (isThrowable) {
            log.error("[DiddQueryChainWrapper][数据库单表数据查询]查询数据不存在:condition:[{}]", formatFilterCondition());
            throw new BizException(warnMessage);
        }
        return null;
    }

    /**
     * @param pageNo      分页参数 页码
     * @param pageSize    分页参数 每页记录数
     * @param resultClass 返回对象实例类型
     * @return
     */
    public SimplePage<?> queryPage(Integer pageNo, Integer pageSize, Class<?> resultClass) {
        IPage<T> pageHelper = new Page<>(pageNo, pageSize);
        pageHelper = this.page(pageHelper);
        return SimplePage.builder(resultClass).page(pageHelper).build();
    }

    /**
     * @param pageNo   分页参数 页码
     * @param pageSize 分页参数 每页记录数
     * @return
     */
    public SimplePage<T> queryPage(Integer pageNo, Integer pageSize) {
        IPage<T> pageHelper = new Page<>(pageNo, pageSize);
        pageHelper = this.page(pageHelper);
        return new SimplePage<>(pageHelper.getTotal(), pageHelper.getRecords());
    }

    /**
     * 查询 列表
     *
     * @return
     */
    public List<T> queryList() {
        return this.list();
    }

    /**
     * 查询 列表
     *
     * @param resultClass 返回对象实例类型
     * @return
     */
    public List<?> queryList(Class<?> resultClass) {
        return BeanUtil.copyList(this.list(), resultClass);
    }


    @SuppressWarnings("unchecked")
    private String formatFilterCondition() {
        AtomicReference<String> condition = new AtomicReference<String>();
        if (MapUtil.isNotEmpty(this.getWrapper().getParamNameValuePairs())) {
            condition.set(this.getWrapper().getExpression().getSqlSegment());
            this.getWrapper().getParamNameValuePairs().forEach((k, v) -> {
                        String conditionValue = v instanceof Number ? v.toString() : "'" + v + "'";
                        condition.set(condition.get().replaceAll("#\\{" + Constants.WRAPPER + ".paramNameValuePairs." + k + "}", conditionValue));
                    }
            );
        }
        return condition.get();
    }

    /* *********************** warper条件封装 *******************************/

    /**
     * 等于 =
     *
     * @param column 字段
     * @param val    值
     * @return
     */
    @SuppressWarnings("unchecked")
    public DidddQueryChainWrapper<T> checkedAndEq(SFunction<T, ?> column, Object val) {
        getWrapper().eq(checkNonNull(val), column, val);
        return typedThis;
    }

    /**
     * 不等于 &lt;&gt;
     *
     * @param column 字段
     * @param val    值
     * @return
     */
    @SuppressWarnings("unchecked")
    public DidddQueryChainWrapper<T> checkedAndNe(SFunction<T, ?> column, Object val) {
        getWrapper().ne(checkNonNull(val), column, val);
        return typedThis;
    }


    /**
     * 大于 &gt;
     *
     * @param column
     * @param val
     * @return
     */
    @SuppressWarnings("unchecked")
    public DidddQueryChainWrapper<T> checkedAndGt(SFunction<T, ?> column, Object val) {
        getWrapper().gt(checkNonNull(val), column, val);
        return typedThis;
    }

    /**
     * 大于等于 &gt;=
     *
     * @param column
     * @param val
     * @return
     */
    @SuppressWarnings("unchecked")
    public DidddQueryChainWrapper<T> checkedAndGe(SFunction<T, ?> column, Object val) {
        getWrapper().ge(checkNonNull(val), column, val);
        return typedThis;
    }

    /**
     * 小于 &lt;
     *
     * @param column
     * @param val
     * @return
     */
    @SuppressWarnings("unchecked")
    public DidddQueryChainWrapper<T> checkedAndLt(SFunction<T, ?> column, Object val) {
        getWrapper().lt(checkNonNull(val), column, val);
        return typedThis;
    }

    /**
     * 小于等于 &lt;=
     *
     * @param column
     * @param val
     * @return
     */
    @SuppressWarnings("unchecked")
    public DidddQueryChainWrapper<T> checkedAndLe(SFunction<T, ?> column, Object val) {
        getWrapper().le(checkNonNull(val), column, val);
        return typedThis;
    }

    /**
     * 字段 IN (v0, v1, ...)
     * <p>例: in("id", 1, 2, 3, 4, 5)</p>
     *
     * <li> 如果动态数组为 empty 则不会进行 sql 拼接 </li>
     *
     * @param column
     * @param valArr
     * @return
     */
    @SuppressWarnings("unchecked")
    public DidddQueryChainWrapper<T> checkedAndIn(SFunction<T, ?> column, Object... valArr) {
        getWrapper().in(checkNonNull(valArr), column, valArr);
        return typedThis;
    }

    /**
     * 字段 IN (value.get(0), value.get(1), ...)
     * <p>例: in("id", Arrays.asList(1, 2, 3, 4, 5))</p>
     *
     * <li> 如果集合为 empty 则不会进行 sql 拼接 </li>
     *
     * @param column
     * @return
     */
    @SuppressWarnings("unchecked")
    public DidddQueryChainWrapper<T> checkedAndIn(SFunction<T, ?> column, Collection<?> valList) {
        getWrapper().in(checkNonNull(valList), column, valList);
        return typedThis;
    }

    /**
     * 字段 IN ( sql语句 )
     * <p>!! sql 注入方式的 in 方法 !!</p>
     * <p>例1: inSql("id", "1, 2, 3, 4, 5, 6")</p>
     * <p>例2: inSql("id", "select id from table where id &lt; 3")</p>
     *
     * @param column    字段
     * @return children
     */
    @SuppressWarnings("unchecked")
    public DidddQueryChainWrapper<T> checkedAndInSql(SFunction<T, ?> column, String inSql) {
        getWrapper().inSql(checkNonNull(inSql), column, inSql);
        return typedThis;
    }


    /**
     * 字段 NOT IN (v0, v1, ...)
     * <p>例: notIn("id", 1, 2, 3, 4, 5)</p>
     *
     * <li> 如果动态数组为 empty 则不会进行 sql 拼接 </li>
     *
     * @param column
     * @param valArr
     * @return
     */
    @SuppressWarnings("unchecked")
    public DidddQueryChainWrapper<T> checkedAndNotIn(SFunction<T, ?> column, Object... valArr) {
        getWrapper().notIn(checkNonNull(valArr), column, valArr);
        return typedThis;
    }

    /**
     * 字段 NOT IN (value.get(0), value.get(1), ...)
     * <p>例: not in("id", Arrays.asList(1, 2, 3, 4, 5))</p>
     *
     *
     * <li> 如果集合为 empty 则不会进行 sql 拼接 </li>
     *
     * @param column
     * @return
     */
    @SuppressWarnings("unchecked")
    public DidddQueryChainWrapper<T> checkedAndNotIn(SFunction<T, ?> column, Collection<?> valList) {
        getWrapper().notIn(checkNonNull(valList), column, valList);
        return typedThis;
    }

    /**
     * BETWEEN 值1 AND 值2
     *
     * @param column 字段
     * @param val1   值1
     * @param val2   值2
     * @return
     */
    @SuppressWarnings("unchecked")
    public DidddQueryChainWrapper<T> checkedAndBetween(SFunction<T, ?> column, Object val1, Object val2) {
        getWrapper().between(checkNonNull(val1) || checkNonNull(val2), column, val1, val2);
        return typedThis;
    }

    /**
     * NOT BETWEEN 值1 AND 值2
     *
     * @param column    字段
     * @param val1      值1
     * @param val2      值2
     * @return
     */
    @SuppressWarnings("unchecked")
    public DidddQueryChainWrapper<T> checkedAndNotBetween(SFunction<T, ?> column, Object val1, Object val2) {
        getWrapper().notBetween(checkNonNull(val1) || checkNonNull(val2), column, val1, val2);
        return typedThis;
    }

    /**
     * 字段 IS NOT NULL
     * <p>例: isNotNull("name")</p>
     *
     * @param column    字段
     * @return
     */
    @SuppressWarnings("unchecked")
    public DidddQueryChainWrapper<T> checkedAndIsNotNull(SFunction<T, ?> column) {
        getWrapper().isNotNull(Boolean.TRUE, column);
        return typedThis;
    }

    /**
     * 字段 IS NULL
     * <p>例: isNull("name")</p>
     *
     * @param column    字段
     * @return
     */
    @SuppressWarnings("unchecked")
    public DidddQueryChainWrapper<T> checkedAndIsNull(SFunction<T, ?> column) {
        getWrapper().isNull(Boolean.TRUE, column);
        return typedThis;
    }

    /**
     * LIKE '%值%'
     *
     * @param column    字段
     * @param val       值
     * @return
     */
    @SuppressWarnings("unchecked")
    public DidddQueryChainWrapper<T> checkedAndLike(SFunction<T, ?> column, Object val) {
        getWrapper().like(checkNonNull(val), column, val);
        return typedThis;
    }

    /**
     * LIKE '%值'
     *
     * @param column    字段
     * @param val       值
     * @return
     */
    @SuppressWarnings("unchecked")
    public DidddQueryChainWrapper<T> checkedAndLikeLeft(SFunction<T, ?> column, Object val) {
        getWrapper().likeLeft(checkNonNull(val), column, val);
        return typedThis;
    }

    /**
     * LIKE '值%'
     *
     * @param column    字段
     * @param val       值
     * @return
     */
    @SuppressWarnings("unchecked")
    public DidddQueryChainWrapper<T> checkedAndLikeRight(SFunction<T, ?> column, Object val) {
        getWrapper().likeRight(checkNonNull(val), column, val);
        return typedThis;
    }


    private Boolean checkNonNull(Object obj) {
        if (obj == null) {
            return Boolean.FALSE;
        }
        return ObjectUtil.isNotEmpty(obj);
    }
}
